import * as Cesium from "cesium";
import Sandcastle from "Sandcastle";

const viewer = new Cesium.Viewer("cesiumContainer", {});
viewer.clock.currentTime = Cesium.JulianDate.fromIso8601(
  "2021-11-09T07:27:37.016064475348684937Z",
);
const scene = viewer.scene;
scene.light.intensity = 7.0;

const cameraTransforms = {
  tileset: {
    destination: new Cesium.Cartesian3(
      4397999.822774582,
      4404502.67774069,
      1397782.4709840622,
    ),
    direction: new Cesium.Cartesian3(
      -0.29335588497705106,
      -0.6066709587467911,
      0.7388454997917905,
    ),
    up: new Cesium.Cartesian3(
      0.6240972421637774,
      0.46391380837591956,
      0.6287182283994301,
    ),
  },
  airport: {
    destination: new Cesium.Cartesian3(
      4394719.151490939,
      4402317.401942875,
      1406608.6602404779,
    ),
    direction: new Cesium.Cartesian3(
      0.4146699515908668,
      -0.8887814163588482,
      0.1952342828060377,
    ),
    up: new Cesium.Cartesian3(
      0.8415067525520951,
      0.4561872920946922,
      0.28941240460723,
    ),
  },
  crater: {
    destination: new Cesium.Cartesian3(
      4398179.160380196,
      4402518.469409466,
      1399161.7612076725,
    ),
    direction: new Cesium.Cartesian3(
      -0.2800903637088597,
      -0.6348021519070498,
      0.7201219452923355,
    ),
    up: new Cesium.Cartesian3(
      0.6319189548885261,
      0.4427783126727723,
      0.6361020360596605,
    ),
  },
  port: {
    destination: new Cesium.Cartesian3(
      4399698.85724341,
      4399019.639078034,
      1405153.7766045567,
    ),
    direction: new Cesium.Cartesian3(
      -0.5651458936543287,
      0.17696574231117793,
      -0.8057873447342694,
    ),
    up: new Cesium.Cartesian3(
      0.4886488937394081,
      0.8587605935024302,
      -0.15411846642958343,
    ),
  },
};

function flyCameraTo(cameraTransform, duration) {
  // Fly to a nice overview of the city.
  viewer.camera.flyTo({
    duration: duration,
    destination: cameraTransform.destination,
    orientation: {
      direction: cameraTransform.direction,
      up: cameraTransform.up,
    },
    easingFunction: Cesium.EasingFunction.QUADRATIC_IN_OUT,
  });
}

// --- Style ---

const buildingStyle = new Cesium.Cesium3DTileStyle({
  color: {
    conditions: [
      ["${HGT} !== undefined && ${HGT} < 5", "color('#f5fd2d')"],
      [
        "${HGT} !== undefined && ${HGT} >= 5 && ${HGT} < 10",
        "color('#d3a34a')",
      ],
      [
        "${HGT} !== undefined && ${HGT} >= 10 && ${HGT} < 15",
        "color('#947e75')",
      ],
      [
        "${HGT} !== undefined && ${HGT} >= 15 && ${HGT} < 20",
        "color('#565a9f')",
      ],
      ["${HGT} !== undefined && ${HGT} > 20", "color('#223bc3')"],
      ["true", "color('white')"],
    ],
  },
});

const terrainStyle = new Cesium.Cesium3DTileStyle({
  color: {
    conditions: [
      ["${name} === 'OCEAN'", "color('#436d9d')"],
      ["${name} === 'LAKE'", "color('#3987c9')"],
      ["${name} === 'CALCAREOUS'", "color('#BBB6B1')"],
      ["${name} === 'GRASS'", "color('#567d46')"],
      ["${name} === 'FOREST'", "color('green')"],
      ["${name} === 'CITY'", "color('lightgray')"],
      ["${name} === 'ASPHALTROAD'", "color('#434343')"],
      ["${name} === 'ASPHALT'", "color('#463d39')"],
      ["${name} === 'CONCRETE'", "color('#b9b4ab')"],
      ["${name} === 'DRYGROUND'", "color('#9B7653')"],
      ["${name} === 'WETGROUND'", "color('#5a4332')"],
      ["${name} === 'SAND'", "color('gold')"],
      ["true", "color('#9B7653')"],
    ],
  },
});

// --- Picking ---

let enablePicking = true;
const handler = new Cesium.ScreenSpaceEventHandler(scene.canvas);

const metadataOverlay = document.createElement("div");
viewer.container.appendChild(metadataOverlay);
metadataOverlay.className = "backdrop";
metadataOverlay.style.display = "none";
metadataOverlay.style.position = "absolute";
metadataOverlay.style.bottom = "0";
metadataOverlay.style.left = "0";
metadataOverlay.style["pointer-events"] = "none";
metadataOverlay.style.padding = "4px";
metadataOverlay.style.backgroundColor = "#303030";
metadataOverlay.style.whiteSpace = "pre-line";
metadataOverlay.style.fontSize = "16px";
metadataOverlay.style.borderRadius = "4px";

let tableHtmlScratch;
let materialsScratch;
let weightsScratch;
let i;

const highlighted = {
  feature: undefined,
  originalColor: new Cesium.Color(),
};

const selected = {
  feature: undefined,
  originalColor: new Cesium.Color(),
};

handler.setInputAction(function (movement) {
  if (enablePicking) {
    // If a feature was previously highlighted, undo the highlight
    if (Cesium.defined(highlighted.feature)) {
      highlighted.feature.color = highlighted.originalColor;
      highlighted.feature = undefined;
    }

    const feature = scene.pick(movement.endPosition);
    const featurePicked = feature instanceof Cesium.Cesium3DTileFeature;

    const isTerrainFeature = featurePicked && feature.hasProperty("substrates");
    const isBuildingFeature = featurePicked && feature.hasProperty("HGT");

    if (isTerrainFeature) {
      metadataOverlay.style.display = "block";
      metadataOverlay.style.bottom = `${
        viewer.canvas.clientHeight - movement.endPosition.y
      }px`;
      metadataOverlay.style.left = `${movement.endPosition.x}px`;

      tableHtmlScratch = `<table><thead><tr><td>Material:</td><th><tt>${feature.getProperty(
        "name",
      )}</tt></tr></thead><tbody>`;

      materialsScratch = feature.getProperty("substrates");
      weightsScratch = feature.getProperty("weights");
      tableHtmlScratch +=
        "<tr><td colspan='2' style='text-align: center;'><b>Substrates</b></td></tr>";

      for (i = 0; i < materialsScratch.length; i++) {
        tableHtmlScratch += `<tr><td><tt>${materialsScratch[i].slice(
          3,
        )}</tt></td><td style='text-align: right;'><tt>${
          weightsScratch[i]
        }%</tt></td></tr>`;
      }
      tableHtmlScratch += "</tbody></table>";
      metadataOverlay.innerHTML = tableHtmlScratch;
    } else {
      metadataOverlay.style.display = "none";
    }

    if (isBuildingFeature) {
      // Highlight the feature if it's not already selected.
      if (feature !== selected.feature) {
        highlighted.feature = feature;
        Cesium.Color.clone(feature.color, highlighted.originalColor);
        feature.color = Cesium.Color.MAGENTA;
      }
    }
  }
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);

handler.setInputAction(function (movement) {
  // If a feature was previously selected, undo the highlight
  if (Cesium.defined(selected.feature)) {
    selected.feature.color = selected.originalColor;
    selected.feature = undefined;
  }

  const feature = scene.pick(movement.position);
  const featurePicked = feature instanceof Cesium.Cesium3DTileFeature;
  const isBuildingFeature = featurePicked && feature.hasProperty("HGT");

  if (isBuildingFeature) {
    // Select the feature if it's not already selected
    if (selected.feature === feature) {
      return;
    }
    selected.feature = feature;

    // Save the selected feature's original color
    if (feature === highlighted.feature) {
      Cesium.Color.clone(highlighted.originalColor, selected.originalColor);
      highlighted.feature = undefined;
    } else {
      Cesium.Color.clone(feature.color, selected.originalColor);
    }
    feature.color = Cesium.Color.LIME;

    tableHtmlScratch = "<table class='cesium-infoBox-defaultTable'>";
    tableHtmlScratch +=
      "<tr><th>Property Name</th><th>ID</th><th>Type</th><th>Value</th></tr><tbody>";
    const metadataClass = feature.content.batchTable._propertyTable.class;
    const propertyIds = feature.getPropertyIds();
    const length = propertyIds.length;
    for (let i = 0; i < length; ++i) {
      const propertyId = propertyIds[i];

      // Skip these properties, since they are always empty.
      if (
        propertyId === "APID" ||
        propertyId === "FACC" ||
        propertyId === "RWID"
      ) {
        continue;
      }

      const propertyValue = feature.getProperty(propertyId);
      const property = metadataClass.properties[propertyId];

      const propertyType = property.componentType ?? property.type;
      tableHtmlScratch += `<tr style='font-family: monospace;' title='${property.description}'><th>${property.name}</th><th><b>${property.id}</b></th><td>${propertyType}</td><td>${propertyValue}</td></tr>`;
    }
    tableHtmlScratch +=
      "<tr><th colspan='4'><i style='font-size:10px'>Hover on a row for description</i></th></tr></tbody></table>";
    viewer.selectedEntity.description = tableHtmlScratch;
  }
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);

// Hide the terrain metadata overlay when the mouse is over the info box, to prevent overlaps.
const infoBoxContainer = document
  .getElementsByClassName("cesium-viewer-infoBoxContainer")
  .item(0);
infoBoxContainer.onmouseover = function (e) {
  metadataOverlay.style.display = "none";
};

// --- UI ---

const locations = [
  {
    text: "Overview",
    onselect: function () {
      flyCameraTo(cameraTransforms.tileset);
    },
  },
  {
    text: "Airport",
    onselect: function () {
      flyCameraTo(cameraTransforms.airport);
    },
  },
  {
    text: "Crater",
    onselect: function () {
      flyCameraTo(cameraTransforms.crater);
    },
  },
  {
    text: "Port",
    onselect: function () {
      flyCameraTo(cameraTransforms.port);
    },
  },
];

function resetHighlight() {
  if (Cesium.defined(selected.feature)) {
    selected.feature.color = selected.originalColor;
    selected.feature = undefined;
  }
  if (Cesium.defined(highlighted.feature)) {
    highlighted.feature.color = highlighted.originalColor;
    highlighted.feature = undefined;
  }
}

try {
  // 3D Tiles 1.1 converted from CDB of Aden, Yemen (CDB provided by Presagis)
  const terrainTileset = await Cesium.Cesium3DTileset.fromIonAssetId(2389063);
  viewer.scene.primitives.add(terrainTileset);
  const buildingsTileset = await Cesium.Cesium3DTileset.fromIonAssetId(
    2389064,
    {
      maximumScreenSpaceError: 12,
    },
  );
  viewer.scene.primitives.add(buildingsTileset);

  viewer.camera.flyTo({
    duration: 0,
    destination: cameraTransforms.tileset.destination,
    orientation: {
      direction: cameraTransforms.tileset.direction,
      up: cameraTransforms.tileset.up,
    },
  });

  const modes = [
    {
      text: "No style",
      onselect: function () {
        resetHighlight();
        buildingsTileset.style = undefined;
        terrainTileset.style = undefined;
      },
    },
    {
      text: "Style buildings based on height",
      onselect: function () {
        resetHighlight();
        buildingsTileset.style = buildingStyle;
        terrainTileset.style = undefined;
      },
    },
    {
      text: "Style terrain based on materials",
      onselect: function () {
        buildingsTileset.style = undefined;
        terrainTileset.style = terrainStyle;
      },
    },
  ];
  Sandcastle.addToolbarMenu(modes);
} catch (error) {
  console.log(`Error loading tileset: ${error}`);
}

Sandcastle.addToolbarMenu(locations);
Sandcastle.addToggleButton(
  "Enable terrain picking",
  enablePicking,
  function (checked) {
    if (enablePicking) {
      metadataOverlay.style.display = "none";
    }

    enablePicking = checked;
  },
);
